﻿using System;
using System.Collections.Generic;
using System.IO;
using Assets.Scripts.Data;
using Test30.Core;
using Test30.Engine;
using Test30.Util;
using UnityEngine;
using Object = UnityEngine.Object;

namespace Assets.Scripts.App
{
    public class GameObjectPool:ObjectPool
    {
        public GameObjectPool(Transform work,Transform idle)
        {
            _work = work;
            _idle = idle;
            if (!_isPoolCreared)
            {
                _isPoolCreared = true;
                CreateIdlePool();
            }
        }

        public void Update()
        {
            foreach (KeyValuePair<PoolItem, object> workPoolObject in WorkPoolObjects)
            {
                if (workPoolObject.Key.Entity != null)
                {
                    var obj = (GameObject) workPoolObject.Key.Go;
                    var entity = workPoolObject.Key.Entity;
                    obj.transform.localPosition= new Vector3(entity.Position.X, entity.Position.Y, 0);
                }
            }
        }

        protected sealed override void CreateIdlePool()
        {
            var pools = LoadGameObjects(Application.dataPath + Configs.PathConact + Configs.ResourcePath +
                                            Configs.EntityPrefabsPath);
            if (pools != null && pools.Count > 0)
            {
                foreach (GameObject gameObject in pools)
                {
                    if (gameObject != null)
                    {
                        var list = new List<PoolItem>();
                        IdlePoolObjects.Add(gameObject.name, list);
                        bool active = gameObject.activeSelf;
                        gameObject.SetActive(false);
                        Transform parent = _idle;

                        var obj = Object.Instantiate(gameObject);
                        obj.name = gameObject.name;
                        obj.transform.SetParent(parent);
                        list.Add(new PoolItem(obj, null, Configs.DefaultDepth, PoolType.Idle));

                        gameObject.SetActive(active);
                    }
                }
            }
        }

        protected override void Recycle(PoolItem obj)
        {
            object prefab;
            if (WorkPoolObjects.TryGetValue(obj, out prefab))
            {
                GameObject go = prefab as GameObject;
                if (go != null)
                {
                    IdlePoolObjects[go.name].Add(obj);
                    go.transform.SetParent(_idle);
                    go.SetActive(false);
                }
                WorkPoolObjects.Remove(obj);
            }
        }

        protected override object Spawn(EntityType type, string letter, Entity entity, int depth)
        {
            List<PoolItem> list;
            GameObject obj = null;
            if (IdlePoolObjects.TryGetValue(GetName(type,letter), out list))
            {
                if (list.Count > 1)
                {
                    while (obj == null && list.Count > 0)
                    {
                        obj = list[0].Go as GameObject;
                        list.RemoveAt(0);
                    }
                    if (obj != null)
                    {
                        AddToWorkPool(obj, entity, depth);
                    }
                }else if (list.Count == 1)
                {
                    GameObject prefab = list[0].Go as GameObject;
                    obj = Object.Instantiate(prefab);
                    if (prefab != null) obj.name = prefab.name;
                    AddToWorkPool(obj, entity, depth);
                }
                else
                {
                    throw new ArgumentOutOfRangeException();
                }
            }
            return obj;
        }

        private void AddToWorkPool(GameObject obj,Entity entity,int depth)
        {
            obj.transform.SetParent(_work);
            obj.transform.localPosition = new Vector3(entity.Position.X,entity.Position.Y,0);
            obj.SetActive(true);
            WorkPoolObjects.Add(new PoolItem(obj, entity, depth, PoolType.Work), obj);
        }

        private string GetName(EntityType type, string letter)
        {
            string name;
            switch (type)
            {
                case EntityType.Monster:
                    name = EntityType.Monster.ToString() + "_" + letter;
                    break;
                case EntityType.Hero:
                    name = EntityType.Hero.ToString() + "_" + letter;
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }
            return name;
        }

        private List<GameObject> LoadGameObjects(string path)
        {
            var list = new List<GameObject>();
            DirectoryInfo info = new DirectoryInfo(path);
            FileInfo[] names = info.GetFiles(Configs.Wildcard + Configs.PrefabFormat);
            foreach (FileInfo fileName in names)
            {
                var name = fileName.Name.RemoveSuffix(Configs.PrefabFormat.Length);
                var go = Resources.Load<GameObject>(Configs.EntityPrefabsPath + Configs.PathConact + name);
                list.Add(go);
            }
            return list;
        }

        private readonly bool _isPoolCreared;
        private readonly Transform _work;
        private readonly Transform _idle;
    }
}
